/// GLSLShader.cpp
/// Matthew DiBernardo (Cryptex)
/// 07.28.09
///
/// See GLSLShader.h for usage information.

#include "GLSLShader.h"
#include <assert.h>

#define MAX_SHADER_LENGTH 8192
static GLubyte shaderText[MAX_SHADER_LENGTH];

void GLSLShader::LoadNextPass(std::string vertexProg, std::string fragmentProg)
{
	m_shaderID.push_back(_LoadShaderPair(vertexProg.c_str(), fragmentProg.c_str()));
}

bool GLSLShader::BindPass(unsigned int _pass)
{
	if(m_shaderID.size() > _pass)
	{
		glUseProgram(m_shaderID[_pass]);

		if(GetHeaderName() == "Assets/Shaders/ToonShader_glsl.sh" && _pass == 0)
		{
			glEnable(GL_CULL_FACE);
			glCullFace(GL_FRONT);
			glPolygonMode(GL_BACK, GL_LINE);
			glLineWidth(4.0);
			//glUseProgram(NULL);
		}

		return true;
	}
	return false;
}

/////////////////////////////////////////////////////////////////
// Load a pair of shaders, compile, and link together. Specify the complete
// path and file name of each ASCII shader file. Note, there is no support for
// just loading say a vertex program... you have to do both.
GLhandleARB GLSLShader::_LoadShaderPair(const char *szVertexProg, const char *szFragmentProg)
	{
    // Temporary Shader objects
    GLhandleARB hVertexShader;
    GLhandleARB hFragmentShader; 
    GLhandleARB hReturn = 0;   
    GLint testVal;
	
    // Create shader objects
    hVertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
    hFragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
	
    // Load them. If fail clean up and return null
    if(bLoadShaderFile(szVertexProg, hVertexShader) == false)
		{
        glDeleteObjectARB(hVertexShader);
        glDeleteObjectARB(hFragmentShader);
        return NULL;
		}
	
    if(bLoadShaderFile(szFragmentProg, hFragmentShader) == false)
		{
        glDeleteObjectARB(hVertexShader);
        glDeleteObjectARB(hFragmentShader);
        return NULL;
		}
    
    // Compile them
    glCompileShaderARB(hVertexShader);
    glCompileShaderARB(hFragmentShader);
    
    // Check for errors
    glGetObjectParameterivARB(hVertexShader, GL_OBJECT_COMPILE_STATUS_ARB, &testVal);
    if(testVal == GL_FALSE)
		{
        glDeleteObjectARB(hVertexShader);
        glDeleteObjectARB(hFragmentShader);
        return NULL;
		}
    
    glGetObjectParameterivARB(hFragmentShader, GL_OBJECT_COMPILE_STATUS_ARB, &testVal);
    if(testVal == GL_FALSE)
		{
        glDeleteObjectARB(hVertexShader);
        glDeleteObjectARB(hFragmentShader);
        return NULL;
		}
    
    // Link them - assuming it works...
    hReturn = glCreateProgramObjectARB();
    glAttachObjectARB(hReturn, hVertexShader);
    glAttachObjectARB(hReturn, hFragmentShader);
    glLinkProgramARB(hReturn);
	
    // These are no longer needed
    glDeleteObjectARB(hVertexShader);
    glDeleteObjectARB(hFragmentShader);  
    
    return hReturn;  
	}

bool GLSLShader::bLoadShaderFile(const char *szFile, GLhandleARB shader)
	{
    GLint shaderLength = 0;
    FILE *fp;
    GLcharARB *fsStringPtr[1];
	
    // Open the shader file
    fopen_s(&fp, szFile, "r");
    if(fp != NULL)
		{
        // See how long the file s
        while (fgetc(fp) != EOF)
            shaderLength++;
		
        // Allocate a block of memory to send in the shader
        assert(shaderLength < MAX_SHADER_LENGTH);   // make me bigger!
        if(shaderLength > MAX_SHADER_LENGTH)
			{
            fclose(fp);
            return false;
			}
		
        // Go back to beginning of file
        rewind(fp);
		
        // Read the whole file in
        if (shaderText != NULL)
            fread(shaderText, 1, shaderLength, fp);
		
        // Make sure it is null terminated and close the file
        shaderText[shaderLength] = '\0';
        fclose(fp);
		}
    else
        return false;
    
	
    // Load the string
    fsStringPtr[0] = (GLcharARB *)shaderText;
    glShaderSourceARB(shader, 1, (const GLcharARB **)fsStringPtr, NULL);
    
    return true;
}